/*
 * Copyright (c) 2024 endless-sky
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef BASE_SRC_BASE_MEM_IMPL_H_
#define BASE_SRC_BASE_MEM_IMPL_H_

#include <mutex>
#include <cstdint>
#include <cstddef>
#include <limits> // Include for std::numeric_limits
#include "utils_mem.h"

namespace El {
namespace Utils {

/// \class BuddyInternal
/// \brief Buddy 内存管理类的内部实现。
class BuddyInternal {
public:
    using Policy = Buddy::Policy;
    using MemoryOperator = Buddy::MemoryOperator;
    using PoolParameter = Buddy::PoolParameter;

    /// 内存块节点定义
    struct ChunkNode {
        uint32_t idx;    ///< 内存块索引。
        int order;       ///< 内存块的阶数，决定其大小（2的幂）。负数表示已分配。
        uint8_t *buf;    ///< 指向实际内存的指针。
        ChunkNode *pre;  ///< 空闲链表中的前一个节点。
        ChunkNode *next; ///< 空闲链表中的后一个节点。
    };

    /// 空闲块链表结构
    struct BufferList {
        ChunkNode head; ///< 特定阶数的空闲链表头节点。
    };

public:
    /// 获取单例实例。
    static BuddyInternal *GetInstance();

    /// 析构函数，释放所有分配的资源。
    ~BuddyInternal() noexcept;

    /// 配置内存管理设置。
    /// \param policy [in] 内存管理策略。
    /// \param memop [in] 自定义内存操作符（nullptr 表示使用默认值）。
    /// \param param [in] 内存池策略参数（nullptr 表示使用默认值）。
    void Configure(Policy policy, MemoryOperator *memop, PoolParameter *param);

    /// 获取管理的总缓冲区大小（字节）。
    size_t GetBufferSize() const;

    /// 获取总空闲内存大小（字节）。
    size_t GetFreeSize() const;

    /// 输出当前内存节点状态以供调试。
    void DumpNodes() const;

    /// 获取内存池缓冲区的起始地址。
    uint8_t *GetPoolBuffer() const
    {
        // 返回对齐后的缓冲区起始地址，即可用内存的起始地址
        return buffer_;
    }

    /// 分配内存。
    /// \param size [in,out] 指向请求大小的指针，将被更新为实际分配的大小。
    /// \return 指向分配内存的指针，失败时返回 nullptr。
    void *Malloc(size_t *size)
    {
        return (this->*malloc_)(size);
    }

    /// 释放先前分配的内存。
    /// \param ptr [in] 要释放的内存指针。
    void Free(void *ptr)
    {
        if (ptr != nullptr) { // 处理前检查空指针
            (this->*free_)(ptr);
        }
    }

    /// 拷贝内存。
    /// \param dest [in] 目标指针。
    /// \param src [in] 源指针。
    /// \param len [in] 要拷贝的字节数。
    /// \return 指向目标的指针。
    void *Memcpy(void *dest, const void *src, size_t len)
    {
        return (this->*memcpy_)(dest, src, len);
    }

    /// 根据配置的对齐方式计算对齐后的大小。
    /// \param size [in] 原始大小。
    /// \return 对齐后的大小。
    size_t GetAlignedSize(size_t size) const
    {
        return (size + alignSize_ - 1) & ~(alignSize_ - 1);
    }

    /// 返回当前的内存分配策略。
    /// \return 当前的内存管理策略。
    Policy GetPolicy() const
    {
        return policy_;
    }

private:
    /// 私有构造函数（用于单例）。
    BuddyInternal();

    // 确保禁用单例的拷贝/移动操作
    BuddyInternal(const BuddyInternal &) = delete;
    BuddyInternal &operator=(const BuddyInternal &) = delete;
    BuddyInternal(BuddyInternal &&) = delete;
    BuddyInternal &operator=(BuddyInternal &&) = delete;

    /// 系统内存分配实现。
    void *SysMalloc(size_t *size)
    {
        // 安全编码：检查 size 是否接近 max_size() 导致潜在溢出
        if (*size > std::numeric_limits<size_t>::max() - alignSize_) {
            // 如果需要在对齐计算前处理潜在溢出
            // 取决于分配器，大值可能无论如何都会失败。
            // memop_.malloc 应能适当地处理大值。
            printf("BuddyInternal::SysMalloc requested size too large: %zu\n", *size);
            return nullptr;
        }
        // 如果底层分配器需要，或我们希望报告一致的对齐方式，确保大小正确对齐。
        // 对于系统 malloc，除了基本类型外，对齐可能无法保证。
        // 假设底层的 memop_.malloc 处理其自身的对齐需求。
        // 我们只返回结果。如果对齐至关重要，请使用 aligned_alloc 或类似方法。
        return memop_.malloc(*size);
    }

    /// 系统内存释放实现。
    void SysFree(void *p)
    {
        memop_.free(p);
    }

    /// 系统内存拷贝实现。
    void *SysMemcpy(void *dest, const void *src, size_t size)
    {
        return memop_.memcpy(dest, src, size);
    }

    /// 系统内存模式初始化。
    void SysInit();

    /// 系统内存模式资源清理。
    void SysDestroy() noexcept; // 标记为 noexcept

    /// 从内存池分配内存。
    /// \param size [in,out] 指向请求大小的指针，将被更新为实际分配的大小。
    /// \return 指向分配内存的指针，失败时返回 nullptr。
    void *PoolMalloc(size_t *size);

    /// 释放从内存池分配的内存。
    /// \param ptr [in] 要释放的内存指针。
    void PoolFree(void *ptr);

    /// 拷贝内存，针对内存池内部拷贝进行了优化。
    /// \param dest [in] 目标指针。
    /// \param src [in] 源指针。
    /// \param size [in] 要拷贝的字节数。
    /// \return 指向目标的指针。
    void *PoolMemcpy(void *dest, const void *src, size_t size);

    // --- 内存池辅助方法 ---

    /// 初始化内存池特定的参数。
    bool InitializePoolParameters();

    /// 为内存池分配主缓冲区和管理结构。
    bool AllocatePoolResources();

    /// 构建内存池的初始空闲链表。
    void BuildFreeLists();

    /// 释放为内存池分配的资源。
    void DestroyPoolResources() noexcept;

    /// 从其空闲链表中移除一个节点。假定已持有锁。
    /// \param node [in] 要删除的节点。
    void DeleteNode(ChunkNode *node);

    /// 将一个节点插入到相应的空闲链表中。假定已持有锁。
    /// \param node [in] 要插入的节点。
    /// \param orderIndex [in] 空闲链表的索引 (order - 1)。
    void InsertNode(ChunkNode *node, int orderIndex);

    /// 输出节点信息以供调试。假定已持有锁。
    void PrintNodeInfo() const;

    /// 计算 floor(log2(x))。
    /// \param x [in] 输入值 (必须 > 0)。
    /// \return floor of log2(x)。如果 x 为 0，则返回 0。
    static int Log2i(uint32_t x);

    /// 如果可用，则合并一个已释放的块及其伙伴块。假定已持有锁。
    /// \param blockIndex [in] 可能要合并的块的索引。
    void Merge(int blockIndex);

    /// 拆分一个较大的块以满足较小的请求。假定已持有锁。
    /// \param targetOrderIndex [in] 期望的阶数索引 (targetOrder - 1)。
    /// \param availableOrderIndex [in] 找到合适块的链表的索引。
    /// \return 分配的目标阶数块的索引。
    int Split(int targetOrderIndex, int availableOrderIndex);

    /// 查找块的伙伴块的索引。
    /// \param blockIndex [in] 当前块的索引。
    /// \param order [in] 当前块的阶数 (1 到 bufferListCount_)。
    /// \return 伙伴块的索引。
    inline int FindBuddyIndex(int blockIndex, int order) const
    {
        // 阶数是基于 1 的 (1 << (order - 1)) 给出块的大小（以 chunk 为单位）
        return blockIndex ^ (1 << (order - 1));
    }

    /// 查找合并后块的索引（总是较小的索引）。
    /// \param blockIndex [in] 正在合并的块之一的索引。
    /// \param order [in] 正在合并的块的阶数 (1 到 bufferListCount_)。
    /// \return 产生的合并块的索引。
    inline int FindCombinedIndex(int blockIndex, int order) const
    {
        // 掩码掉伙伴之间不同的位
        return blockIndex & (~(1 << (order - 1)));
    }

    /// 检查指针是否在管理的内存池范围内。
    template <typename PtrType, typename StartType>
    inline bool IsPointerInPool(PtrType *ptr, StartType *poolStart, size_t poolSize) const
    {
        const auto ptrAddr = reinterpret_cast<uintptr_t>(ptr);
        const auto startAddr = reinterpret_cast<uintptr_t>(poolStart);
        // 安全编码：检查结束地址计算中潜在的溢出
        uintptr_t endAddr;
        if (startAddr > std::numeric_limits<uintptr_t>::max() - poolSize) {
            // 将发生溢出
            return false;
        }
        endAddr = startAddr + poolSize;
        return ptrAddr >= startAddr && ptrAddr < endAddr;
    }

private:
    // 内存池特定成员
    uint8_t *originBuffer_{nullptr};  ///< 原始分配的内存起始地址（对齐前）。
    uint8_t *buffer_{nullptr};        ///< 对齐后的内存起始地址（可用内存池的开始）。
    ChunkNode *chunkHead_{nullptr};   ///< 用于管理的块节点数组。
    size_t chunkMask_{0};             ///< 块大小的掩码 (chunkSize - 1)。
    int chunkNodeLog2_{0};            ///< chunkSize 的 Log2 值。
    BufferList *bufferList_{nullptr}; ///< 空闲链表头数组。
    int chunkCount_{0};               ///< 总块数。
    int bufferListCount_{0};          ///< 空闲链表数量 (maxOrder + 1)。
    mutable std::mutex mutex_;        ///< 用于线程安全的互斥锁（mutable 允许在 const 方法中使用）。

    // 通用成员
    size_t alignSize_{4}; ///< 内存对齐大小。

    // 当前策略的函数指针
    void *(BuddyInternal::*malloc_)(size_t *size){&BuddyInternal::SysMalloc}; ///< 当前分配函数指针。
    void (BuddyInternal::*free_)(void *ptr){&BuddyInternal::SysFree};         ///< 当前释放函数指针。
    void *(BuddyInternal::*memcpy_)(void *dest, const void *src, size_t len){
        &BuddyInternal::SysMemcpy}; ///< 当前内存拷贝函数指针。

    Policy policy_{Policy::kDefault}; ///< 当前内存管理策略。
    // 默认内存操作符使用标准库函数
    MemoryOperator memop_{.malloc = ::malloc, .free = ::free, .memcpy = ::memcpy};
    // 默认内存池参数
    PoolParameter param_{.totalSize = 1024 * 1024 * 2, .chunkSize = 1024, .alignSize = 4};
};

} // namespace Utils
} // namespace El

#endif // BASE_SRC_BASE_MEM_IMPL_H_
